﻿using System;
using System.Data;
using System.Data.SqlClient;
using System.Reflection;
using System.Collections.Generic;
using System.Text;

namespace ScanSystemLogForm.DAL
{
    public class dalFactory<T> where T : class, new()
    {
        //获取表标识列
        //TableName　数据表名称
        private List<string> TableIdentity(string TableName)
        {
            //建立字符串列表  List<string>
            List<string> myList = new List<string>();
            //数据库数据库ＳＱＬ语名 强记系统自带SQL语句 
            string strSql = @"select name from syscolumns where COLUMNPROPERTY( id,name,'IsIdentity')=1 and id=object_id('" + TableName + "')";
            //Using自动释放对象
            //新建 sqldatareader对象,用来存放查询出来的数据
            using (SqlDataReader myReder = dbHelper.SelectReader(strSql))
            {
                //myReader.Read() 循环所有查询的出来的数据  Read() 表示读取下一条
                while (myReder.Read())
                {
                    //把第一列,添加到List列表
                    myList.Add(myReder[0].ToString());
                }
                //返回所有表识列列表
                return myList;
            }
        }

        /// <summary>
        /// 返回表主键列表
        /// </summary>
        /// <param name="tableName">表名</param>
        /// <returns>List</returns>
        private List<string> PrimaryKeyList(string tableName)
        {
            //主键名称列表
            List<string> myList = new List<string>();
            //新建用来保存 表名的sql参数 @table_name  tableName  表示:表名
            SqlParameter par = new SqlParameter("@table_name", tableName);
            //新建用来保存 

            //建参数列表,保存sql参数
            List<SqlParameter> myParList = new List<SqlParameter>();
            //把sql参数,添加参数列表中 
            myParList.Add(par);
            //uinsg 自动释放对象
            //sqlDataReader保存查询出来的数据
            //sp_pkeys 表示:用来查询表主键的存储过程, 是系统自定义的
            //myParList 表示:存储过程用到的 sql参数列表, 
            //true    表示: SelectReader方法,调用的是存储过程
            using (SqlDataReader myRead = dbHelper.SelectReader("sp_pkeys", myParList, CommandType.StoredProcedure))
            {
                //循环读取下一条记录
                while (myRead.Read())
                {
                    //把记录添加到主键集合
                    myList.Add(myRead["column_name"].ToString());
                }
            }
            //返回主键列表
            return myList;
        }

        /// <summary>
        /// 插入数据,返回主键值
        /// </summary>
        /// <param name="obj">实体对象</param>
        /// <returns>受影响数据</returns>
        /// T 表示:实例类传进来的类型 如:new  DalBase<Model.meun>  那 T就=meun
        public int Insert(T obj)
        {
            //获取类型
            Type T = obj.GetType();
            //获取标识列列表
            List<string> primList = TableIdentity(T.Name);
            //获取主键
            List<string> PkList = PrimaryKeyList(T.Name);
            //获取类型属性
            PropertyInfo[] pArr = T.GetProperties();
            //新建SQL参数列表,用来保存插入SQL语句用的SQL参数
            List<SqlParameter> myParList = new List<SqlParameter>();
            //用来保存SQl语句 列名的字符串 如:id,name,sex,age
            string strK = "";
            //用来保存sql语句 列名值的字符串 如:@id,@name,@sex,@age
            string strV = "";
            //循环遍历类中每个属性  pArr 表示:类所有属性  p 表示:单个属性 如:id,name
            foreach (PropertyInfo p in pArr)
            {
                //判断属性不是标识列 !primList.Contains(p.Name)
                //再次判断属性不为空  p.GetValue(obj, null)!=null
                //如果属性不为标识列且不为空,就组装 把:列名,SQL参数,SQl参数值  添加到SQL语句当中
                if (!primList.Contains(p.Name) && p.GetValue(obj, null) != null)
                {
                    //组装列名 如strk+strK+p.Name+","
                    //P.Name  表示:单个属性的名称   与数据库列名一致  最后结果如:id,name,sex,age,
                    strK += p.Name + ",";
                    //@+P.Name 表示:单个属性在Sql语句用到的SQL变量  如:@id  最后结果如:@id,@name,@sex,@age,
                    strV += "@" + p.Name + " ,";
                    //新建SQL变量对象保存 属性在SQL语句中用到Sql参数 如:@id 
                    //"@"+p.Name 表示:@加上p的属性名称 
                    //p.GetValue 表示:获取对象ojb的属性内容值  null 表示:此属性不为集合是单个变量
                    SqlParameter par = new SqlParameter("@" + p.Name, p.GetValue(obj, null));
                    //把Sql变量添加到SQL变量列表当中
                    myParList.Add(par);
                }
            }
            //由于组装strK值的时间后面多一个"," 所有要去掉最后一个字符
            //strK!="" 判断字符是否为空
            if (strK != "")
            {
                //subtring获取字串 0:表示 从0下标开始   strk.Length-1 表示:要获取的总字符数
                strK = strK.Substring(0, strK.Length - 1);
            }
            //由于组装strK值的时间后面多一个"," 所有要去掉最后一个字符
            if (strV != "")
            {
                //subtring获取字串 0:表示 从0下标开始   strk.Length-1 表示:要获取的总字符数
                strV = strV.Substring(0, strV.Length - 1);
            }

            //StringBuilder 字串符对象,比Stirng 相加 的效率高很多 
            StringBuilder SB = new StringBuilder();
            //Append 相当于string 的相加    
            //obj.GetType().Name  表示:获取表的名称 由于类的名称与数据库表一致
            SB.Append("insert into " + obj.GetType().Name + "  (");
            //strK 表示:列名 如:id,name
            SB.Append(strK + ")");
            //output inserted:表示返回表中的某列; PkList[0]的值一般为主键列的列名称,例如: id
            SB.Append(" output inserted." + PkList[0] + "  values (");

            //strV 表示:列名SQl变量 如:@id,@name
            SB.Append(strV + ")");
            //List列表,转成数组
            //由于CUD的方法,只接收SqlParameter数组,下面把List<SqlParameter> 转换SQL数组;
            SqlParameter[] myArrPar = myParList.ToArray();
            //调用CUD增,删,改通用方法 返回受影响的总数
            //SB.ToString 表示:SQl语句  myArrPar 表示:SQL参数数组
            return dbHelper.ExcuteSQL(SB.ToString(), myArrPar);
        }


        /**
         目的:组装修改SQL语名
              update 表名 Set 列名=@列名  where 主列名=@主键列名
        */


        /// <summary>
        /// 更新一条数据,返回受影响的行数
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public int Update(T obj)
        {
            //获取类型
            Type T = obj.GetType();
            //获取主键列表  由于修改时不能修改主键,要把主键做为修改条件 所以要把主键列查出来
            List<string> primList = PrimaryKeyList(T.Name);
            //获取标识列表  由于修改时不能修改标识列,所以要把标识查出来,不参与修改语句组装
            List<string> identityList = TableIdentity(T.Name);
            //获取类型的所有属性,以便遍历
            PropertyInfo[] pArr = T.GetProperties();

            //新建SQl参数列表,用保存Sql语句调用的SQL参数
            List<SqlParameter> myParList = new List<SqlParameter>();
            //用来保存组装SQL语句列名 如:id=@id,name=@name,sex=@sex
            string strK = "";
            //用来保存SQL语句组装时的条件 如:id=@id and name=@name
            string strWhere = "";

            //遍历类所有属性,来组装 列名(strK),条件(strWhere),SQl参数
            //pArr表示:所有属性,  P 表示:单个属性
            foreach (PropertyInfo p in pArr)
            {
                //由于主键不能修改,要用来做修改语句的条件所以要判断属性是否存在主键中
                //判断属性是否存在主键中 !primList.Contains(p.Name)
                //p.Name 表示:p的属性名
                if (!primList.Contains(p.Name))
                {
                    //判断属性是否存在标识中 原因:数据库标识列不能修改
                    //!identityList.Contains(p.Name)  判断p的属性值是否存在标识列表中
                    //判断属性内容是否为空,原因:属性值为空的话,就没有必要修改
                    // p.GetValue(obj, null) != null
                    if (!identityList.Contains(p.Name) && p.GetValue(obj, null) != null)
                    {
                        //组装SQl修改部分  最后结果如:name=@name,sex=@sex
                        strK += p.Name + "=@" + p.Name + ",";
                        //把SQL修改部分用到的SQ变量,添加到SQL变量列表中
                        //p.GetValue(obj, null) 表示:获取对象ojb的属性内容值  null:表示些属性不是集合
                        SqlParameter par = new SqlParameter("@" + p.Name, p.GetValue(obj, null));
                        myParList.Add(par);
                    }
                }
                else
                {
                    //如果属性值不存在主键中,则会成为条件一部分 如:where 
                    //组装条件 p.Name 表示:属性名称
                    strWhere += " and " + p.Name + "=@" + p.Name;
                    //把条件语句Where 用到的SQL参数变量添加到SQl变量集中
                    SqlParameter par = new SqlParameter("@" + p.Name, p.GetValue(obj, null));
                    myParList.Add(par);
                }
            }

            //判断是SQl修改语句是否为空
            if (strK != "")
            {
                //由于SQl修改语名后面带,所以要去掉
                strK = strK.Substring(0, strK.Length - 1);
            }

            //StringBuilder 表示:字符串对象,比string 的效率高的多 
            StringBuilder SB = new StringBuilder();
            //Append 追加字符串,相当中string 的加号(+)
            //obj.GetType().Name  表示:获取类型名,由于类型名与表名一致,相当于获取表名
            SB.Append("update " + obj.GetType().Name + " set ");
            SB.Append(strK);
            SB.Append(" where 1=1 ");
            SB.Append(strWhere);
            //由于CUD方法只接收SqlParameter[]数组,所以要转换
            SqlParameter[] myArrPar = myParList.ToArray();
            //调用CUD方法,返回受影响总数
            return dbHelper.ExcuteSQL(SB.ToString(), myArrPar);
        }

        /******************************************************
         * 目的:组装SQl删除语句
         *      delete 表名  where 主键列=@主键列名
         * 
         ******************************************************/
        //T 表示:实例化 DalBase<T> 对象传入的类型  如:new DalBase<Model.Menu> T就代表Menu  
        public int Delete(T obj)
        {
            //获取类型
            Type T = obj.GetType();
            //获取主键列表
            List<string> primList = PrimaryKeyList(T.Name);
            //获取类型所有属性    T.GetProperties()
            PropertyInfo[] pArr = T.GetProperties();
            //创建SQL变是列表,用来保存SQL语句用到的SQL变量
            List<SqlParameter> myParList = new List<SqlParameter>();
            //用来保存SQL条件的字符串  如: id=@id and name=@name
            string strWhere = "";
            //循环遍历属性集合 pArr 表示:所有属性 p 表示:单个属性值
            foreach (PropertyInfo p in pArr)
            {
                //判断属性是否存在主键列表中,如果存在则,组装条件语句  
                //原因:删除语句只需要条件语句 如:where id=@id
                if (primList.Contains(p.Name))
                {
                    //判断是主键内容值是否为空,空不则不组装
                    if (p.GetValue(obj, null) != null)
                    {
                        //组装Sql条件语名,最后结果如:and id=@id and name=@name,
                        strWhere += " and " + p.Name + "=@" + p.Name + ",";
                        //把SQl条件语句用到SQL参数,添加到SQL参数列表中
                        //p.GetValue(obj, null) 表示:获取obj对象的属性内容值 null:此属性不为集合
                        SqlParameter par = new SqlParameter("@" + p.Name, p.GetValue(obj, null));
                        myParList.Add(par);
                    }
                }
            }

            //判断是条件语句是否为空
            if (strWhere != "")
            {  //去掉条件语句后面的","号
                strWhere = strWhere.Substring(0, strWhere.Length - 1);
            }

            //创建StringBuilder对象,来组装SQL语名 原因:比string效率高很多
            StringBuilder SB = new StringBuilder();
            // obj.GetType().Name 获取类型名称,由于类型名与数据库表名一致,就相当于获取表名
            SB.Append("delete " + obj.GetType().Name);
            SB.Append(" where 1=1 ");
            SB.Append(strWhere);
            //把SQL参数列表,转成SQL参数数组
            SqlParameter[] myArrPar = myParList.ToArray();
            //调用CUD,增,删 ,改通用方法,返回受影响的数量
            return dbHelper.ExcuteSQL(SB.ToString(), myArrPar);
        }

        //批量删除 
        public int Delete(List<T> obj)
        {
            int j = 0;
            for (int i = 0; i < obj.Count; i++)
            {
                j += Delete(obj[i]);
            }
            return j;
        }

        //批量删除 
        public int Delete(string strSql)
        {
            return dbHelper.ExcuteSQL(strSql);
        }


        /// <summary>
        /// 返回单个对象
        /// </summary>
        /// <param name="strWhere">查询条件　如: and id=10 and name='中国'</param>
        /// <returns>单个对象</returns>
        public T SelectFirstOrDefault(string strWhere)
        {
            List<T> myList = Select(strWhere, null);
            if (myList.Count > 0)
            {
                return myList[0];
            }
            else
            {
                //返回初始值，什么都没有；
                return default(T);
            }
        }

        /// <summary>
        /// 单表查询　自动生成返回列
        /// </summary>
        /// <param name="strWhere">查询条件 如:and id>@id and name=@name 没有则为:Null</param>
        /// <param name="WhereSqlParameter">Sql参数变量 没有则为:Null</param>
        /// <returns></returns>
        public List<T> Select(string strWhere, List<SqlParameter> WhereSqlParameter)
        {
            List<T> myList = new List<T>();
            try
            {
                //获取类型
                Type U = typeof(T);
                //获取类型属性
                PropertyInfo[] pArr = U.GetProperties();
                //获取Sql参数
                List<SqlParameter> myParList = new List<SqlParameter>();
                string strK = "";
                foreach (PropertyInfo p in pArr)
                {
                    strK += p.Name + ",";
                }
                if (strK != "")
                {
                    strK = strK.Substring(0, strK.Length - 1);
                }

                StringBuilder SB = new StringBuilder();
                SB.Append("select ");
                SB.Append(strK);
                SB.Append(" from " + U.Name);
                if (strWhere != null && strWhere != "")
                {
                    SB.Append("  where 1=1 " + strWhere);
                }
                using (SqlDataReader myReader = dbHelper.SelectReader(SB.ToString(), WhereSqlParameter))
                {
                    while (myReader.Read())
                    {
                        //实例化一个类型对象
                        Type myType = typeof(T);
                        //跟据类型创建对象
                        object obj = Activator.CreateInstance(myType);
                        //循环列所有例
                        for (int i = 0; i < myReader.FieldCount; i++)
                        {
                            //得到对映的属性
                            //myReader.GetName(i)  获取列名
                            PropertyInfo pro = myType.GetProperty(myReader.GetName(i));
                            object val = myReader[i];
                            //判断对象是否为空,属性是否为空,属性是否可写!
                            if (val != DBNull.Value && pro != null && pro.CanWrite)
                            {
                                //给属性赋值
                                //obj 指对象
                                //myReader[i]对象的值
                                //null 不是为索引
                                pro.SetValue(obj, myReader[i], null);
                            }

                        }
                        myList.Add((T)obj);
                    }
                }

                return myList;
            }
            catch (Exception ex)
            {

                return null;
            }

        }
        ///<summary>
        ///单表查询
        ///</summary>
        ///<param name="strSql">sql语句 如:selec * from aa where id=1</param>
        ///<returns>list</returns>
        public List<T> Select(string strSql)
        {
            List<T> myList = new List<T>();
            try
            {
                List<SqlParameter> myParList = new List<SqlParameter>();

                using (SqlDataReader myReader = dbHelper.SelectReader(strSql))
                {
                    while (myReader.Read())
                    {
                        //实例化一个类型对象
                        Type myType = typeof(T);
                        //跟据类型创建对象
                        object obj = Activator.CreateInstance(myType);
                        //循环列所有例
                        for (int i = 0; i < myReader.FieldCount; i++)
                        {
                            //得到对映的属性
                            //myReader.GetName(i)  获取列名
                            PropertyInfo pro = myType.GetProperty(myReader.GetName(i));
                            object val = myReader[i];
                            //判断对象是否为空,属性是否为空,属性是否可写!
                            if (val != DBNull.Value && pro != null && pro.CanWrite)
                            {
                                //给属性赋值
                                //obj 指对象
                                //myReader[i]对象的值
                                //null 不是为索引
                                pro.SetValue(obj, myReader[i], null);
                            }

                        }
                        myList.Add((T)obj);
                    }
                }

                return myList;
            }
            catch (Exception ex)
            {

                return null;
            }

        }

        /// <summary>
        /// 多表查询  注意：列结果要与对象属性名一致
        /// </summary>
        /// <param name="strSql">Sql语名 如:select id,name from tableName 注意id,name要与属性名一致</param>
        /// <param name="strWhere">Where条件  如:id=@id and name=@name</param>
        /// <param name="WhereSqlParameter">SqlParameterList列表 说明:没有参数则为 null</param>
        /// <returns>List对象集合</returns>
        public List<T> Select(string strSql, string strWhere, List<SqlParameter> WhereSqlParameter)
        {
            List<T> myList = new List<T>();
            try
            {
                //获取类型
                Type U = typeof(T);
                //获取类型属性
                PropertyInfo[] pArr = U.GetProperties();
                //获取Sql参数
                List<SqlParameter> myParList = new List<SqlParameter>();
                string strK = "";
                foreach (PropertyInfo p in pArr)
                {
                    strK += p.Name + ",";
                }
                if (strK != "")
                {
                    strK = strK.Substring(0, strK.Length - 1);
                }

                StringBuilder SB = new StringBuilder();
                SB.Append(strSql);
                if (strWhere != null && strWhere != "")
                {
                    SB.Append("  where 1=1 " + strWhere);
                }
                using (SqlDataReader myReader = dbHelper.SelectReader(SB.ToString(), WhereSqlParameter))
                {
                    while (myReader.Read())
                    {
                        //实例化一个类型对象
                        Type myType = typeof(T);
                        //跟据类型创建对象
                        object obj = Activator.CreateInstance(myType);
                        //循环列所有例
                        for (int i = 0; i < myReader.FieldCount; i++)
                        {
                            //得到对映的属性
                            //myReader.GetName(i)  获取列名
                            PropertyInfo pro = myType.GetProperty(myReader.GetName(i));
                            object val = myReader[i];
                            //判断对象是否为空,属性是否为空,属性是否可写!
                            if (val != DBNull.Value && pro != null && pro.CanWrite)
                            {
                                //给属性赋值
                                //obj 指对象
                                //myReader[i]对象的值
                                //null 不是为索引
                                pro.SetValue(obj, myReader[i], null);
                            }

                        }
                        myList.Add((T)obj);
                    }
                }

                return myList;
            }
            catch (Exception ex)
            {

                return null;
            }

        }

        // 分页公式
        //select top {0} * from TeacherInfo where UserID not in(select top {1} UserID from TeacherInfo )",pageSize,(index-1)*pageSize


        /// <summary>
        /// 分页通用方法
        /// </summary>
        /// <param name="strWhere">查询条件</param>
        /// <param name="WhereSqlParameter">查询条件SQl参数</param>
        /// <param name="pageSize">页面大小</param>
        /// <param name="page">当前页</param>
        /// <param name="count">总数行</param>
        /// <returns></returns>
        public List<T> Select(string strWhere, List<SqlParameter> WhereSqlParameter, int pageSize, int page, ref int count)
        {

            List<T> myList = new List<T>();
            try
            {
                //获取类型
                Type U = typeof(T);
                //获取类型属性
                PropertyInfo[] pArr = U.GetProperties();
                //获取Sql参数
                List<SqlParameter> myParList = new List<SqlParameter>();
                string strK = "";
                foreach (PropertyInfo p in pArr)
                {
                    strK += p.Name + ",";
                }
                if (strK != "")
                {
                    strK = strK.Substring(0, strK.Length - 1);
                }

                StringBuilder countSb = new StringBuilder();
                countSb.Append("select count(*) from " + U.Name);
                if (strWhere != null && strWhere != "")
                {
                    countSb.Append("  where 1=1 " + strWhere);
                }

                using (SqlDataReader countReader = dbHelper.SelectReader(countSb.ToString()))
                {
                    //向下读一条记录
                    countReader.Read();
                    //获取总行数
                    count = countReader[0] == null ? 0 : Convert.ToInt32(countReader[0]);
                }

                //获取表主键
                List<string> myPrim = PrimaryKeyList(U.Name);
                StringBuilder SB = new StringBuilder();
                SB.Append("select top " + pageSize + "  ");
                SB.Append(strK);
                SB.Append(" from " + U.Name);

                //主键总数是否>0用来 判断是:单表还是多表查询
                if (myPrim.Count > 0)
                {
                    if (strWhere != null && strWhere != "")
                    {
                        SB.Append("  where 1=1 and " + myPrim[0] + " not in (select top " + pageSize * (page - 1) + " " + myPrim[0] + " from " + U.Name + "   order by " + myPrim[0] + " )  " + strWhere + "  order by  " + myPrim[0]);
                    }
                    else
                    {
                        SB.Append("  where 1=1 and " + myPrim[0] + " not in (select top " + pageSize * (page - 1) + " " + myPrim[0] + " from " + U.Name + "   order by " + myPrim[0] + ")  order by  " + myPrim[0]);
                    }
                }
                else
                {
                    //视图查询
                    if (strWhere != null && strWhere != "")
                    {
                        SB.Append("  where 1=1 and " + pArr[0].Name + " not in (select top " + pageSize * (page - 1) + " " + pArr[0].Name + " from " + U.Name + "  order by " + pArr[0].Name + " )  " + strWhere + "  order by  " + pArr[0].Name);
                    }
                    else
                    {
                        SB.Append("  where 1=1 and " + pArr[0].Name + " not in (select top " + pageSize * (page - 1) + " " + pArr[0].Name + " from " + U.Name + "   order by " + pArr[0].Name + ")  order by  " + pArr[0].Name);
                    }
                }

                using (SqlDataReader myReader = dbHelper.SelectReader(SB.ToString(), WhereSqlParameter))
                {
                    while (myReader.Read())
                    {
                        //实例化一个类型对象
                        Type myType = typeof(T);
                        //跟据类型创建对象
                        object obj = Activator.CreateInstance(myType);
                        //循环列所有例
                        for (int i = 0; i < myReader.FieldCount; i++)
                        {
                            //得到对映的属性
                            //myReader.GetName(i)  获取列名
                            PropertyInfo pro = myType.GetProperty(myReader.GetName(i));
                            object val = myReader[i];
                            //判断对象是否为空,属性是否为空,属性是否可写!
                            if (val != DBNull.Value && pro != null && pro.CanWrite)
                            {
                                //给属性赋值
                                //obj 指对象
                                //myReader[i]对象的值
                                //null 不是为索引
                                pro.SetValue(obj, myReader[i], null);
                            }
                        }
                        myList.Add((T)obj);
                    }
                }

                return myList;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

    }
}